import WebGl from '@/modules/three';
// import fontJson from 'three/examples/fonts/helvetiker_regular.typeface.json'
const fontJson = require('three/examples/fonts/helvetiker_regular.typeface.json');

const { Three, TWEEN, dat, Stats } = WebGl;

class WebGlControl {
  private container: any;
  private camera: any;
  private scene: any;
  private renderer: any;
  private stats: any;
  private loader: any;
  private tween: any;
  private animateFrame: any;
  private particleSystem: any; //

  private Geometrys: {
    cubeGeometry?: any;
    starsGeometry?: any;
  } = {}; //保存所有Geometry，方便统一销毁
  private Materials: {
    cubeMaterial?: any;
    starsMaterial?: any;
  } = {}; //保存所有Material，方便统一销毁

  private GuiInit: any = null;
  private guiData: any = {
    color: '#ffffff',
    mouseMove: false,
    autoRotate: true,
    SEPARATION: 100,
    AMOUNTX: 50,
    AMOUNTY: 50,
    positionYBase: 50, //粒子y晃动幅度
  };
  private mouseData: any = {
    mouseX: 0,
    mouseY: 0,
  };

  public init = () => {
    // if(this.scene) {return}
    this.container = document.getElementById('WebGL-points-gpu');
    let width = this.container.clientWidth,
      height = this.container.clientHeight;

    this.scene = new Three.Scene();

    this.camera = new Three.PerspectiveCamera(40, width / height, 1, 10000);
    this.camera.position.z = 150;
    this.camera.lookAt(this.scene.position);

    this.renderer = new Three.WebGLRenderer();
    // this.renderer.setClearColor( 0xffffff );
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(width, height);
    this.container.appendChild(this.renderer.domElement);

    this.loader = new Three.ObjectLoader(); //JSONLoader 已经移除了

    this.addThings();
    this.addLight(); //+光源
    this.animate(); //开始动
    this.initEventListen();
    this.initStats();

    this.initGui();
  };
  public destroy = () => {
    this.toggleGui(0);
    Object.values(this.Geometrys).forEach((e) => {
      e.dispose();
    });
    Object.values(this.Materials).forEach((e) => {
      e.dispose();
    });
    this.scene.clear();
    this.renderer.dispose();
    this.renderer.forceContextLoss();
    this.camera = null;
    this.scene = null;
    this.renderer = null;
    this.GuiInit = null;
    cancelAnimationFrame(this.animateFrame);

    this.particleSystem = null;
    this.tween.stop();
    this.tween = null;
  };

  public initGui = () => {
    if (this.GuiInit) {
      this.toggleGui();
      return;
    }
    this.GuiInit = new dat.GUI({
      // autoPlace: false
    });

    this.GuiInit.domElement.style.position = 'absolute';
    this.GuiInit.domElement.style.right = '10px';
    this.GuiInit.domElement.style.top = '10px';

    let GuiController = this.GuiInit.addFolder('控制窗口');
    // GuiController.addColor(this.guiData, "color").name('粒子颜色');
    // GuiController.add(this.guiData, "mouseMove").name('鼠标晃动');
    GuiController.open();

    // let starsGui = this.GuiInit.addFolder("stars position");
    // starsGui.add(this.particleSystem.position, 'x', -500, 500, 1);
    // starsGui.add(this.particleSystem.position, 'y', -500, 500, 1);
    // starsGui.add(this.particleSystem.position, 'z', -500, 500, 1);
    // starsGui.open()

    // let starsGuiRotate = this.GuiInit.addFolder("stars rotation");
    // starsGuiRotate.add(this.guiData, "autoRotate").name('自动旋转');
    // starsGuiRotate.add(this.particleSystem.rotation, 'x', -3.14, 3.14, 0.01);
    // starsGuiRotate.add(this.particleSystem.rotation, 'y', -3.14, 3.14, 0.01);
    // starsGuiRotate.add(this.particleSystem.rotation, 'z', -3.14, 3.14, 0.01);
    // //rotation是欧拉类型的值，单位是π；比如让物体按Y轴正方向旋转45°可以写成 object3D.rotation.y+=0.25*Math.PI
    // starsGuiRotate.open()

    // let otherGui = this.GuiInit.addFolder("其他");
    // otherGui.add(this.guiData, 'positionYBase', 20, 100, 1).name('晃动幅度');
    // otherGui.open()
  };
  private initStats = () => {
    this.stats = new Stats();
    this.stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    this.stats.dom.style.position = 'absolute';
    this.container.appendChild(this.stats.dom);
  };

  public toggleGui = (state: any = null) => {
    if (!this.GuiInit || !this.GuiInit.domElement) {
      return;
    }
    let display = this.GuiInit.domElement.style.display == 'none' ? '' : 'none';
    if (state == 0) {
      display = 'none';
    }
    this.GuiInit.domElement.style.display = display;
  };

  private initEventListen = () => {
    this.container.addEventListener('pointermove', this.onPointerMove);
  };
  private onPointerMove = (event) => {
    if (event.isPrimary === false) return;
    this.mouseData.mouseX = event.clientX - this.container.clientWidth;
    this.mouseData.mouseY = event.clientY - this.container.clientHeight;
  };

  private addLight = () => {
    let light = new Three.DirectionalLight(0xffffff);
    light.position.set(0, 200, 0);
    this.scene.add(light);
  };

  private addThings = () => {
    // const axesHelper = new Three.AxesHelper( 800 );
    // this.scene.add( axesHelper )  //添加坐标轴；红线是X轴，绿线是Y轴，蓝线是Z轴

    var obj1: any, obj2: any, loaded: boolean;
    this.loader.load('../../static/teapot-claraio.json', (obj) => {
      console.log('teapot-claraio', obj);
      // obj.applyMatrix4(new Three.Matrix4().makeTranslation(0, -10, 0));
      obj1 = obj;
      if (obj1 && obj2 && !loaded) {
        loaded = true;
        this.addPartice(obj1, obj2);
      }
    });
    this.loader.load('../../static/cpmovie-edit.json', (obj) => {
      console.log('cpmovie-edit', obj);
      // obj.applyMatrix4(new Three.Matrix4().makeRotationX(Math.PI/2));
      // obj.applyMatrix4(new Three.Matrix4().makeTranslation(0, 10, 0));
      obj.rotation.x = Math.PI / 2; //THREE.Math.degToRad(30);
      obj2 = obj;
      if (obj1 && obj2 && !loaded) {
        loaded = true;
        this.addPartice(obj1, obj2);
      }
    });

    /** 粒子 **/
  };

  private addPartice = (obj1, obj2) => {
    console.log('???', obj1, obj2);
    var moreObj, lessObj;
    if (
      obj1.geometry.attributes.position.array.length >
      obj2.geometry.attributes.position.array.length
    ) {
      moreObj = obj1;
      lessObj = obj2;
    } else {
      moreObj = obj2;
      lessObj = obj1;
    }
    var position1: any[] = [];
    var position2: any[] = [];
    var moreLen = moreObj.geometry.attributes.position.array.length;
    var lessLen = lessObj.geometry.attributes.position.array.length;

    position1 = moreObj.geometry.attributes.position.array;
    position2 = [...lessObj.geometry.attributes.position.array];

    for (var i = lessLen, j = 0; i < moreLen; i++, j++) {
      position2.push(position2[j]); //要把少的补成跟多的一样的长度
    }

    /* 有一个模型需要rotate一下，用矩阵操作 */
    const vector = new Three.Vector3(1, 1, 1); //新建一个三维向量
    let matrix = new Three.Matrix4(); //新建一个4*4矩阵
    matrix.makeRotationX(Math.PI / 2); //这是一个 rotateX=90° 的矩阵
    matrix.multiply(new Three.Matrix4().makeScale(2, 2, 2)); //乘上一个 scale=2 的矩阵
    matrix.multiply(new Three.Matrix4().makeTranslation(30, 0, -10)); //乘上一个 tranxlateX=50 的矩阵
    // let changedVector = vector.applyMatrix4(matrix)  //将该向量乘以四阶矩阵m，计算转换后的坐标；
    // console.log('Matrix::::', vector, matrix, changedVector)
    //需要循环对每一个点进行计算，放下面循环里面了
    /* 有一个模型需要rotate一下，用矩阵操作 */

    let sizes: any[] = [];
    // let colors:any[] = []
    const color = new Three.Color();
    for (let i = 0; i < moreLen / 3; i++) {
      sizes.push(4);
      color.setHSL(0.01 + 0.1 * (i / 400), 0.99, 0.25 + 0.1 * (i / 400));
      // colors=[...colors, ...color.toArray()]

      /* 有一个模型需要rotate一下，用矩阵操作 */
      /*但是这一段循环操作太慢了！！！！！*/
      // let x = moreObj.geometry.attributes.position.array[i*3]
      // let y = moreObj.geometry.attributes.position.array[i*3+1]
      // let z = moreObj.geometry.attributes.position.array[i*3+2]
      // const vector = new Three.Vector3(x,y,z);
      // let resArr = vector.applyMatrix4(matrix).toArray()
      // position1 = [...position1, ...resArr]
      /* 有一个模型需要rotate一下，用矩阵操作 */
    }

    // console.log('pos22222??', position1, position2)

    this.particleSystem = new Three.Group();
    this.Geometrys.starsGeometry = new Three.BufferGeometry();

    const texture = new Three.TextureLoader().load('../../dot.png');
    var uniforms = {
      color: { value: new Three.Color(0xffffff) },
      pointTexture: { value: texture },
      val: { value: 1.0 },
    };

    this.Materials.starsMaterial = new Three.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: `
        attribute float size;
        attribute vec3 position2;
        uniform float val;
        varying vec3 vPos;

        void main() {

          vPos.x = position.x * val + position2.x * (1.-val);
          vPos.y = position.y* val + position2.y * (1.-val);
          vPos.z = position.z* val + position2.z * (1.-val);

          vec4 mvPosition = modelViewMatrix * vec4( vPos, 1.0 );

          gl_PointSize = size * ( 300.0 / -mvPosition.z );

          gl_Position = projectionMatrix * mvPosition;

        }
      `,
      fragmentShader: `
        uniform vec3 color;
        uniform sampler2D pointTexture;

        varying vec3 vPos;

        void main() {

          vec3 vColor = vec3(1.0, 0., 0.);
          vColor.r = vPos.z/50.;
          vColor.g = vPos.y/50.;
          vColor.b = vPos.x/50.;

          gl_FragColor = vec4( color * vColor, 1.0 );

          gl_FragColor = gl_FragColor * texture2D( pointTexture, gl_PointCoord );

        }
      `,

      blending: Three.AdditiveBlending,
      depthTest: false,
      transparent: true,
    });

    this.Geometrys.starsGeometry.setAttribute(
      'size',
      new Three.Float32BufferAttribute(sizes, 1),
    );
    this.Geometrys.starsGeometry.setAttribute(
      'position',
      new Three.Float32BufferAttribute(position1, 3),
    );
    this.Geometrys.starsGeometry.setAttribute(
      'position2',
      new Three.Float32BufferAttribute(position2, 3),
    );

    var particle = new Three.Points(
      this.Geometrys.starsGeometry,
      this.Materials.starsMaterial,
    );
    this.particleSystem.add(particle);
    this.particleSystem.position.y = -15;

    this.Geometrys.starsGeometry.applyMatrix4(matrix); //直接旋转整个模型···· 这样就行了

    this.scene.add(this.particleSystem);

    this.initTween();
  };

  private initTween = () => {
    var pos: any = { val: 1 };
    this.tween = new TWEEN.Tween(pos)
      .to({ val: 0 }, 3000)
      .easing(TWEEN.Easing.Quadratic.InOut)
      .delay(2000)
      .onUpdate(callback);
    let tweenBack = new TWEEN.Tween(pos)
      .to({ val: 1 }, 3000)
      .easing(TWEEN.Easing.Quadratic.InOut)
      .delay(2000)
      .onUpdate(callback);
    this.tween.chain(tweenBack);
    tweenBack.chain(this.tween);
    this.tween.start();

    // p = position1;
    let that = this;
    function callback(res) {
      // console.log('!!:', res.val)
      that.Materials.starsMaterial.uniforms.val.value = res.val;
    }
  };

  private animate = () => {
    // if(!this.renderer){return;}
    if (this.stats) {
      this.stats.begin();
    }

    this.render();
    TWEEN.update();

    if (this.stats) {
      this.stats.end();
    }
    this.animateFrame = requestAnimationFrame(this.animate);
  };

  private render = () => {
    // this.renderer.setClearColor(this.guiData.color,1.0);
    // if(this.guiData.autoRotate && this.particleSystem){
    // this.particleSystem.rotation.x+= 0.025
    // this.particleSystem.rotation.y+= 0.005
    // }

    if (this.guiData.mouseMove) {
      this.camera.position.x +=
        (-this.mouseData.mouseX - this.camera.position.x) * 0.015;
      this.camera.position.y +=
        (-this.mouseData.mouseY - this.camera.position.y) * 0.015;
    }

    var time = Date.now() * 0.005;
    if (this.particleSystem) {
      var bufferObj = this.Geometrys.starsGeometry;
      this.particleSystem.rotation.y = 0.01 * time;

      var sizes = bufferObj.attributes.size.array;
      var len = sizes.length;

      for (var i = 0; i < len; i++) {
        sizes[i] = 2 * (1.0 + Math.sin(0.02 * i + time));
      }
      this.Geometrys.starsGeometry.setAttribute(
        'size',
        new Three.Float32BufferAttribute(sizes, 1),
      );
    }

    this.renderer.render(this.scene, this.camera);
  };
}

const webGlControl = new WebGlControl();
export default webGlControl;
